package com.zysdev.playh264

import android.media.MediaCodec
import android.media.MediaFormat
import android.util.Log
import android.view.Surface
import java.io.*
import java.lang.Exception

class H264Player(private val path: String, surface: Surface) : Runnable {
//object H264Player {

    private var mediaCodec: MediaCodec? = null


    init {
        try {
//            h265  --ISO hevc  兼容 硬编   不兼容   电视    -----》8k  4K
            try {
                mediaCodec = MediaCodec.createDecoderByType("video/avc")
            } catch (e: Exception) {
                e.printStackTrace()
//                不支持硬编
                Log.e("---", "---手机dsp不支持这种硬编码，去使用软编码")
            }
            val format = MediaFormat.createVideoFormat("video/avc", 368, 384)
            format.setInteger(MediaFormat.KEY_FRAME_RATE, 15)
            mediaCodec!!.configure(format, surface, null, 0)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }


    fun play() {
        mediaCodec?.start()
        Thread(this).start()
    }


    override fun run() {
        try {
            decodeH264()
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e("---", "run: $e")
        }

    }

    private fun decodeH264() {
        var bytes: ByteArray? = null
        try {
//            偷懒   文件  加载内存     文件 1G  1G
            bytes = getBytes(path!!)
        } catch (e: Exception) {
            e.printStackTrace()
        }
        if (bytes == null) {
            Log.e("---", "---fileBytes=null")
            return
        }
        if (bytes.isEmpty()) {
            Log.e("---", "---fileBytes.isEmpty")
            return
        }

        var startIndex = 0
        val totalSize = bytes.size
//        Log.e("---", "---totalSize=$totalSize")
        while (true) {
            if (totalSize == 0 || startIndex >= totalSize) {//开始帧位置大于文件buffer array的长度
                break
            }
//
            //下一个I帧的起始位置
            val nextFrameStart = getNextFrameIndex(bytes, startIndex + 2, totalSize)
//
            val info = MediaCodec.BufferInfo()
            val inIndex = mediaCodec!!.dequeueInputBuffer(10000)
            //放数据
            if (inIndex >= 0) {
                val byteBuffer =  mediaCodec!!.getInputBuffer(inIndex) ?: continue
                byteBuffer.clear()
                byteBuffer.put(bytes, startIndex, nextFrameStart - startIndex)
                mediaCodec!!.queueInputBuffer(
                    inIndex, 0, nextFrameStart - startIndex,
                    System.currentTimeMillis(), 0
                )
                startIndex = nextFrameStart
            } else {
                continue
            }

            //取解码后的yuv数据
            var outIndex: Int = mediaCodec!!.dequeueOutputBuffer(info, 10000)
//            注意 这里的outIndex，不要写成inIndex了！！！！！
            while (outIndex >= 0) {
                try {
                    Thread.sleep(33)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
                val doRender = info.size != 0

                mediaCodec!!.releaseOutputBuffer(outIndex, doRender)
                outIndex = mediaCodec!!.dequeueOutputBuffer(info, 10000)
            }
        }
        mediaCodec!!.stop()
        mediaCodec!!.release()


    }

    private fun getNextFrameIndex(bytes: ByteArray, start: Int, totalSize: Int): Int {

        for (i in start..totalSize - 4) {
            if (bytes[i] == 0x00.toByte() && bytes[i + 1] == 0x00.toByte() && bytes[i + 2] == 0x00.toByte() && bytes[i + 3] == 0x01.toByte()
                || bytes[i] == 0x00.toByte() && bytes[i + 1] == 0x00.toByte() && bytes[i + 2] == 0x01.toByte()
            ) {
                return i
            }
        }
        return -1
    }

    private fun getBytes(path: String): ByteArray? {
        val inputStream: InputStream = DataInputStream(FileInputStream(File(path)))
        var len: Int
        val size = 1024
        val bos = ByteArrayOutputStream()
        var buf = ByteArray(size)
        while (inputStream.read(buf, 0, size).also { len = it } != -1) {
            bos.write(buf, 0, len)
        }
        buf = bos.toByteArray()
        return buf
    }
}